package com.sjb.cp.manager.websocket;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.sjb.cp.manager.websocket.disruptor.PictureEditEventProducer;
import com.sjb.cp.model.enums.PictureEditActionEnum;
import com.sjb.cp.model.enums.PictureEditMessageTypeEnum;
import com.sjb.cp.model.po.User;
import com.sjb.cp.service.impl.UserServiceImpl;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author:sjb
 * @CreateTime:2025-02-20
 * @Description: webSocket处理器
 * @Version：1.0
 */
@Component
public class PictureEditHandler extends TextWebSocketHandler {

    // 每张图片的编辑状态，key: pictureId, value: 当前正在编辑的用户 ID
    private final Map<Long, Long> pictureEditingUsers = new ConcurrentHashMap<>();

    // 保存所有连接的会话，key: pictureId, value: 用户会话集合
    private final Map<Long, Set<WebSocketSession>> pictureSessions = new ConcurrentHashMap<>();

    private final UserServiceImpl userServiceImpl;

    @Resource
    private PictureEditEventProducer pictureEditEventProducer;

    public PictureEditHandler(UserServiceImpl userServiceImpl) {
        this.userServiceImpl = userServiceImpl;
    }


    /**
     * 编辑图片时的广播消息
     *
     * @param pictureId
     * @param pictureEditResponseMessage
     * @param excludeSession
     */
    private void broadcastToPicture(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage,
                                    WebSocketSession excludeSession) throws IOException {

        Set<WebSocketSession> webSocketSessions = pictureSessions.get(pictureId);
        if (CollectionUtil.isNotEmpty(webSocketSessions)) {
            ObjectMapper objectMapper = new ObjectMapper();
            // 配置序列化 将Long转为String类型
            SimpleModule module = new SimpleModule();
            module.addSerializer(Long.class, ToStringSerializer.instance);
            // 支持 long 基本类型
            module.addSerializer(Long.TYPE, ToStringSerializer.instance);
            objectMapper.registerModule(module);
            String message = objectMapper.writeValueAsString(pictureEditResponseMessage);
            TextMessage textMessage = new TextMessage(message);
            for (WebSocketSession webSocketSession : webSocketSessions) {
                // 排除掉的session不发送
                if (excludeSession != null && excludeSession.equals(webSocketSession)) {
                    continue;
                }
                if (webSocketSession.isOpen()) {
                    webSocketSession.sendMessage(textMessage);
                }
            }
        }
    }

    /**
     * 全部广播 不排除session
     *
     * @param pictureId
     * @param pictureEditResponseMessage
     * @throws Exception
     */
    private void broadcastToPicture(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage) throws Exception {
        broadcastToPicture(pictureId, pictureEditResponseMessage, null);
    }


    /**
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(@NotNull WebSocketSession session) throws Exception {
        User user = (User) session.getAttributes().get("userId");
        Long pictureId = (Long) session.getAttributes().get("pictureId");

        pictureSessions.putIfAbsent(pictureId, ConcurrentHashMap.newKeySet());
        pictureSessions.get(pictureId).add(session);

        // 构造响应
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
        String message = String.format("%s进入编辑", user.getUserName());

        pictureEditResponseMessage.setMessage(message);
        pictureEditResponseMessage.setUserVo(userServiceImpl.getUserVo(user));
        // 广播给同一张图片的用户
        broadcastToPicture(pictureId, pictureEditResponseMessage, session);
    }


    /**
     * 处理消息
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    public void handleTextMessage(@NotNull WebSocketSession session, TextMessage message) throws Exception {
        // 将消息解析为PictureEditMessage
        PictureEditRequestMessage pictureEditRequestMessage =
                JSONUtil.toBean(message.getPayload(),PictureEditRequestMessage.class);
        String type = pictureEditRequestMessage.getType();
        PictureEditMessageTypeEnum pictureEditMessageTypeEnum = PictureEditMessageTypeEnum.valueOf(type);

        Long pictureId = (Long) session.getAttributes().get("pictureId");
        User user = (User)session.getAttributes().get("user");

        // 发布消息
        pictureEditEventProducer.publishEvent(pictureEditRequestMessage,session,user,pictureId);

        // 调用对应的消息处理方法
        switch (pictureEditMessageTypeEnum){
            case ENTER_EDIT:
                handleEditMessage(pictureEditRequestMessage,session,user,pictureId);
                break;
            case EDIT_ACTION:
                handleEditActionMessage(pictureEditRequestMessage,session,user,pictureId);
                break;
            case EXIT_EDIT:
                handleExitEditMessage(pictureEditRequestMessage,session,user,pictureId);
                break;
            default:
                PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
                pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.ERROR.getValue());
                pictureEditResponseMessage.setMessage("消息类型错误");
                pictureEditResponseMessage.setUserVo(userServiceImpl.getUserVo(user));
                session.sendMessage(new TextMessage(JSONUtil.toJsonStr(pictureEditResponseMessage)));
        }
    }

    /**
     * 处理编辑消息
     * @param pictureEditRequestMessage
     * @param session
     * @throws Exception
     */
    public void handleEditMessage(PictureEditRequestMessage pictureEditRequestMessage,
                                   WebSocketSession session,User loginUser,Long pictureId) throws Exception {

        // 没有用户进入编辑该图片 才能进入编辑
        if (pictureEditingUsers.containsKey(pictureId)){
            // 设置当前用户未编辑用户
            pictureEditingUsers.put(pictureId, loginUser.getId());
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.ENTER_EDIT.getValue());
            String message = String.format("%s开始编辑图片", loginUser.getUserName());
            pictureEditResponseMessage.setMessage(message);
            pictureEditResponseMessage.setUserVo(userServiceImpl.getUserVo(loginUser));
            broadcastToPicture(pictureId, pictureEditResponseMessage);
        }
    }

    /**
     * 同步编辑消息 不再同步他人
     * @param pictureEditRequestMessage
     * @param session
     * @param loginUser
     * @param pictureId
     */
    public void handleEditActionMessage(PictureEditRequestMessage pictureEditRequestMessage,
                                         WebSocketSession session,User loginUser,Long pictureId) throws IOException {

        Long editingUserId = pictureEditingUsers.get(pictureId);
        String editAction = pictureEditRequestMessage.getEditAction();

        PictureEditActionEnum enumByValue = PictureEditActionEnum.getEnumByValue(editAction);
        if (enumByValue == null){
            return;
        }

        if (editingUserId.equals(loginUser.getId())){
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.EDIT_ACTION.getValue());
            pictureEditResponseMessage.setUserVo(userServiceImpl.getUserVo(loginUser));
            String message = String.format("%s正在执行%s",loginUser.getUserName(),enumByValue.getText());
            pictureEditResponseMessage.setMessage(message);
            pictureEditResponseMessage.setEditAction(editAction);
            // 广播给除了当前客户端的其他客户
            broadcastToPicture(pictureId,pictureEditResponseMessage,session);
        }
    }

    /**
     * 处理退出编辑转态
     * @param pictureEditRequestMessage
     * @param session
     * @param loginUser
     * @param pictureId
     */
    public void handleExitEditMessage(PictureEditRequestMessage pictureEditRequestMessage,
                                       WebSocketSession session,User loginUser,Long pictureId) throws Exception {

        Long editPictureId = pictureEditingUsers.get(pictureId);
        if (editPictureId != null && loginUser.getId().equals(editPictureId)){
            PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
            pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.EXIT_EDIT.getValue());
            pictureEditResponseMessage.setUserVo(userServiceImpl.getUserVo(loginUser));
            String message = String.format("%s退出编辑",loginUser.getUserName());
            pictureEditResponseMessage.setMessage(message);
            broadcastToPicture(pictureId,pictureEditResponseMessage);
        }
    }

    /**
     * 退出连接后 关闭会话等信息
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(@NotNull WebSocketSession session, @NotNull CloseStatus status) throws Exception {
        Map<String, Object> attributes = session.getAttributes();
        User user = (User)attributes.get("user");
        Long pictureId = (Long)attributes.get("pictureId");

        // 移除当前用户的编辑状态
        handleExitEditMessage(null,session,user,pictureId);

        // 删除会话
        Set<WebSocketSession> webSocketSessions = pictureSessions.get(pictureId);
        if (CollectionUtil.isNotEmpty(webSocketSessions)) {
            webSocketSessions.remove(session);
            if (webSocketSessions.isEmpty()){
                pictureSessions.remove(pictureId);
            }
        }

        // 响应
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue());
        pictureEditResponseMessage.setUserVo(userServiceImpl.getUserVo(user));
        String message = String.format("%s退出编辑",user.getUserName());
        pictureEditResponseMessage.setMessage(message);
        broadcastToPicture(pictureId,pictureEditResponseMessage);
    }

}
